package com.itheima.train.manager.order.service;

import com.alibaba.fastjson.JSON;
import com.itheima.train.manager.domain.*;
import com.itheima.train.manager.order.fegin.OrderFeignClient;
import com.itheima.train.manager.order.mq.RabbitmqProducer;
import com.itheima.train.manager.utils.SnowFlakeGenerator;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @program: itheima-train-manager-parent
 * @description: 订单的处理
 * @author: zhanghz001
 * @create: 2021-07-05 16:04
 **/

public class ZhzOrderServiceImpl {
    // 日志记录器对象
    private Logger log = LoggerFactory.getLogger(OrderService.class);
    // 定义前端参数和座位性质之间的Map集合
    private static Map<String, String> seatNatureHashMap = new HashMap<String, String>();
    
    static {
        seatNatureHashMap.put("busSeat", "2");
        seatNatureHashMap.put("firstSeat", "3");
        seatNatureHashMap.put("secondSeat", "4");
        seatNatureHashMap.put("hardSleeper", "7");
        seatNatureHashMap.put("softSleeper", "6");
        seatNatureHashMap.put("hardSeat", "5");
        seatNatureHashMap.put("softSeat", "9");
    }
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 创建一个雪花算法对应的类对象
    private SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator(0, 2);
    
    // 创建一个线程池
    private ExecutorService executorService = new ThreadPoolExecutor(10, 10,
            0L, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(10));
    
    @Autowired
    private RabbitmqProducer rabbitmqProducer;
    
    @Autowired
    private OrderFeignClient orderFeignClient;
    
    @Autowired
    private InterProcessMutex lock;
    
    /**
     * 下单操作
     *
     * @param orderParams
     * @return
     */
    public ResponseResult<String> submitOrder(OrderParams orderParams) {
        //构造返回对象
        ResponseResult<String> result = new ResponseResult<>();
        
        //获取相关参数
        String userId = orderParams.getUserId();
        String trainNum = orderParams.getTrainNum();
        String trainRidingDate = orderParams.getTrainRidingDate();
        Long ridingPlanId = orderParams.getRidingPlanId();
        String ticketType = orderParams.getTicketType();
        String redisTicketTypeKey = ticketType + "Stock";
        
        log.info("【预扣库存开始了】 ---> " + JSON.toJSONString(orderParams));
        //进行预扣库存
        // 构建乘车计划的 key
        // riding_plan:G26:2020-06-01:270101019289976832
        
        String ridingPlanDateKey = "riding_plan:" + trainNum + ":" + trainRidingDate + ":" + ridingPlanId;
        //获取当前计划的剩余数量
        int count = Integer.parseInt(redisTemplate.boundHashOps(ridingPlanDateKey).get(ridingPlanDateKey).toString());
        if (count < 0) {
            result.setResult(false);
            return result;
        }
        
        //动态扣减库存
        String stockCountKeyDynamic = "riding_plan:" + trainNum + ":" + trainRidingDate + ":*";
        Set<String> keys = redisTemplate.keys(stockCountKeyDynamic);
        for (String key : keys) {
            redisTemplate.boundHashOps(key).put(redisTicketTypeKey, String.valueOf(count - 1));
        }
        //
        log.info("【预扣库存结束了】 ---> " + JSON.toJSONString(orderParams));
        
        //--------------------------------------------------------------------------
        //分配座位开始
        log.info("【分配座位开始了】 ---> " + JSON.toJSONString(orderParams));
        
        //
        // 分配座位: train_seat:C1002:2020-06-01:4:6
        //获取到指定座位的所有车厢的key
        String seatNature = seatNatureHashMap.get(ticketType);
        String seatNatureKey = "train_seat:" + trainNum + ":" + trainRidingDate + ":"
                + seatNature + ":*";
        Set<String> allSearchNatureCoach = redisTemplate.keys(seatNatureKey);
        
        //车厢号,座位在车厢的位置
        String seatNo = null;
        String coach = null;
        //遍历所有车厢,寻找到是否还有剩余的指定类型的座位(比如二等座)
        out:
        for (String key : allSearchNatureCoach) {
            
            //获取每一个车厢的所有座位
            Map<Object, Object> entries = redisTemplate.boundHashOps(key).entries();
            coach = entries.get("coach_num").toString();
            
            //每节车厢的座位
            for (Object seatNoKey : entries.keySet()) {
                //查找每个座位对应的状态值
                String seatStatus = entries.get(seatNoKey).toString();
                
                //状态如果是0,记录座位标号
                if ("0".equals(seatStatus)) {
                    seatNo = seatNoKey.toString();
                    break out;
                }
            }
            
        }
        log.info("【分配座位结束了】 --->coach--->{},seatNo---->{} ", coach, seatNo);
        
        //--------------------------生成订单------------------------------------------------
        // riding_plan:G26:2020-06-01:270101019289976832
        //获取所有乘车计划数据,并将其封装到一个 TbRidingPlanDate 对象中
        Map<Object, Object> ridingPlanDateHashMap = redisTemplate.boundHashOps(ridingPlanDateKey).entries();
        String ridingPlanDateString = JSON.toJSONString(ridingPlanDateHashMap);
        TbRidingPlanDate tbRidingPlanDate = JSON.parseObject(ridingPlanDateString, TbRidingPlanDate.class);
        
        //生成订单
        String finalCoachNum = coach;
        String finalSeatNum = seatNo;
        Callable<ResponseResult<String>> task = () -> {
            //创建订单开始            
            log.info("【构建订单开始了】 ---> " + JSON.toJSONString(orderParams));
            long orderId = snowFlakeGenerator.nextId();
            
            //构建订单数据
            Order order = new Order();
            order.setId(orderId);
            order.setCoachNum(finalCoachNum);
            order.setIsEffect("1");
            order.setPayStatus("0");
            order.setRidingPlanDateId(orderParams.getRidingPlanId());
            order.setSeatNature(seatNature);
            order.setSeatNum(finalSeatNum);
            order.setTrainNum(orderParams.getTrainNum());
            order.setUserId(orderParams.getUserId());
            order.setTrainRidingDate(orderParams.getTrainRidingDate());
            order.setStartStationName(tbRidingPlanDate.getStartStationName());
            order.setReceiveStationName(tbRidingPlanDate.getReceiveStationName());
            
            if ("终点站".equals(tbRidingPlanDate.getStartTime())) {
                order.setStartTime(tbRidingPlanDate.getReceiveStartStationTime());
            } else {
                order.setStartTime(tbRidingPlanDate.getStartTime());
            }
            order.setEndTime(tbRidingPlanDate.getReceiveEndStationTime());
            
            //根据座位情况设置具体的票价
            if ("7".equals(seatNature)) { // 硬卧
                if (finalSeatNum.contains("上铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperUpPrice());
                } else if (finalSeatNum.contains("中铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperMiddlePrice());
                } else if (finalSeatNum.contains("下铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getHardSleeperDownPrice());
                }
            } else if ("6".equals(seatNature)) { // 软卧
                if (finalSeatNum.contains("上铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getSoftSleeperUpPrice());
                } else if (finalSeatNum.contains("下铺")) {
                    order.setSeatPrice(tbRidingPlanDate.getSoftSleeperDownPrice());
                }
            } else if ("3".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getFirstSeatPrice());
            } else if ("4".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getSecondSeatPrice());
            } else if ("2".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getBusSeatPrice());
            } else if ("5".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getHardSeatPrice());
            } else if ("9".equals(seatNature)) {
                order.setSeatPrice(tbRidingPlanDate.getSoftSeatPrice());
            }
            log.info("【构建订单结束了】 ---> " + JSON.toJSONString(orderParams));
            //--------------------------redis排队------------------------------------------------
            
            log.info("【redis排队开始了】 ---> " + JSON.toJSONString(orderParams));
            //采用 Redis 中的 ZSet 集合存储排队信息，使用：列车编号 + 乘车日期 + 用户 id 作为 key，
            // 使用当前系统时间的纳秒值作为 value
            String sortedKey = trainNum + ":" + trainRidingDate + ":" + orderParams.getUserId();
            redisTemplate.boundZSetOps("train_manager:sorted_queue").add(sortedKey, System.nanoTime());
            log.info("【redis排队结束了】 ---> " + JSON.toJSONString(orderParams));
            //--------------------------es同步数据------------------------------------------------
            
            log.info("【发送es同步数据开始了】 ---> " + JSON.toJSONString(order));
            OrderSynEs orderSynEs = new OrderSynEs();
            orderSynEs.setRidingPlanDate(trainRidingDate);
            orderSynEs.setSeatNature(seatNature);
            orderSynEs.setTrainNum(trainNum);
            rabbitmqProducer.sendMessage(JSON.toJSONString(orderSynEs), "syn_stock");
            log.info("【发送es同步数据结束了】 ---> " + JSON.toJSONString(orderSynEs));
            
            //--------------------------订单数据发送,用来通知用户------------------------------------------------
            log.info("【发送订单数据开始了】 ---> " + JSON.toJSONString(orderSynEs));
            
            String userString = redisTemplate.boundHashOps("train_manager:userInfo")
                    .get(orderParams.getUserId()).toString();
            User user = JSON.parseObject(userString, User.class);
            OrderHandler orderHandler = new OrderHandler();
            orderHandler.setOrder(order);
            orderHandler.setUser(user);
            //发送数据
            rabbitmqProducer.sendMessage(JSON.toJSONString(orderHandler), "gen_order");
            log.info("【发送订单数据结束了】 ---> " + JSON.toJSONString(orderSynEs));
            
            //
            result.setResult(true);
            return result;
        };
        try {
            Future<ResponseResult<String>> future = executorService.submit(task);
            ResponseResult<String> result1 = future.get();
            return result1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        //--------------------------------------------------------------------------
        return result;
    }
    
    /**
     * 查询队列信息
     *
     * @param orderParams
     * @return
     */
    public ResponseResult<String> querySortedInfo(OrderParams orderParams) {
        
        //创建ResponseResult对象
        ResponseResult<String> responseResult = new ResponseResult<>();
        
        //从redis中获取排队信息
        
        //--------------------------从redis中获取排队信息------------------------------------------------
        log.info("【获取排队信息开始了】 ---> " + JSON.toJSONString(orderParams));
        
        //获取相关参数
        String userId = orderParams.getUserId();
        String trainNum = orderParams.getTrainNum();
        String trainRidingDate = orderParams.getTrainRidingDate();
        Long ridingPlanId = orderParams.getRidingPlanId();
        String ticketType = orderParams.getTicketType();
        
        String sortedKey = trainNum + ":" + trainRidingDate + ":" + orderParams.getUserId();
        //获取到队列最前面的key
        Long rank = redisTemplate.boundZSetOps("train_manager:sorted_queue")
                .rank(sortedKey);
        log.info("rank --->{}", rank);
        
        //判断结果
        if (rank == null) {
            responseResult.setResult(false);
        } else {
            responseResult.setResult(true);
            responseResult.setData(String.valueOf(rank));
        }
        
        log.info("【获取排队信息结束了】 ---> " + JSON.toJSONString(orderParams));
        
        //返回
        return responseResult;
    }
    
    public OrderHandler queryById(Long orderId) {
        OrderHandler orderHandler = new OrderHandler();
        //通过feign接口获取到订单
        Order order = orderFeignClient.queryById(orderId);
        
        //通过redis 获取到用户
        String userString = redisTemplate
                .boundHashOps("train_manager:userInfo")
                .get(orderId).toString();
        User user = JSON.parseObject(userString, User.class);
        orderHandler.setUser(user);
        orderHandler.setOrder(order);
        return orderHandler;
    }
}
